package com.neo.tiny.bpm.flowable.listener;

import cn.hutool.core.util.StrUtil;
import com.google.common.collect.ImmutableSet;
import com.neo.tiny.admin.api.user.AdminUserApi;
import com.neo.tiny.admin.dto.user.UserInfoDTO;
import com.neo.tiny.bpm.dto.message.BpmMessageSendWhenTaskCreatedReqDTO;
import com.neo.tiny.bpm.entity.BpmTaskExtDO;
import com.neo.tiny.bpm.enums.instance.BpmProcessInstanceDeleteReasonEnum;
import com.neo.tiny.bpm.enums.task.BpmProcessInstanceResultEnum;
import com.neo.tiny.bpm.mapper.BpmTaskExtMapper;
import com.neo.tiny.bpm.message.BpmMessageService;
import com.neo.tiny.common.util.NumberUtils;
import com.neo.tiny.query.LambdaQueryWrapperBase;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
import org.flowable.engine.delegate.event.FlowableActivityCancelledEvent;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Set;

/**
 * @Description: 监听 {@link org.flowable.task.api.Task} 的开始与完成，创建与更新对应的 {@link com.neo.tiny.bpm.entity.BpmTaskExtDO} 记录
 * @Author: yqz
 * @CreateDate: 2022/10/15 19:30
 */
@Slf4j
@Component
public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {

    @Resource
    private BpmTaskExtMapper taskExtMapper;

    @Resource
    private AdminUserApi adminUserApi;

    @Resource
    private BpmMessageService messageService;

    @Lazy
    @Resource
    private HistoryService historyService;

    @Resource
    @Lazy
    private RuntimeService runtimeService;

    public static final Set<FlowableEngineEventType> TASK_EVENTS = ImmutableSet.<FlowableEngineEventType>builder().add(FlowableEngineEventType.TASK_CREATED).add(FlowableEngineEventType.TASK_ASSIGNED).add(FlowableEngineEventType.TASK_COMPLETED).add(FlowableEngineEventType.ACTIVITY_CANCELLED).build();

    public BpmTaskEventListener() {
        super(TASK_EVENTS);
    }

    /**
     * 创建任务
     *
     * @param event 任务事件
     */
    @Override
    protected void taskCreated(FlowableEngineEntityEvent event) {

        BpmTaskExtDO ext = taskToTaskExt((Task) event.getEntity());
        ext.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
        taskExtMapper.insert(ext);
    }

    /**
     * 完成任务
     *
     * @param event 任务事件
     */
    @Override
    protected void taskCompleted(FlowableEngineEntityEvent event) {
        BpmTaskExtDO ext = taskToTaskExt((Task) event.getEntity());
        // 不设置也问题不大，因为 Complete 一般是审核通过，已经设置
        ext.setResult(BpmProcessInstanceResultEnum.APPROVE.getResult());
        ext.setEndTime(LocalDateTime.now());
        taskExtMapper.update(ext, new LambdaQueryWrapperBase<BpmTaskExtDO>().eq(BpmTaskExtDO::getTaskId, ext.getTaskId()));
    }

    /**
     * 分配任务
     *
     * @param event 任务事件
     */
    @Override
    protected void taskAssigned(FlowableEngineEntityEvent event) {
        Task task = (Task) event.getEntity();
        BpmTaskExtDO ext = new BpmTaskExtDO();
        ext.setAssigneeUserId(NumberUtils.parseUpperCaseLong(task.getAssignee()));
        ext.setTaskId(task.getId());
        taskExtMapper.update(ext, new LambdaQueryWrapperBase<BpmTaskExtDO>().eq(BpmTaskExtDO::getTaskId, task.getId()));
        // 发送通知。在事务提交时，批量执行操作，所以直接查询会无法查询到 ProcessInstance，所以这里是通过监听事务的提交来实现。

        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {

                ProcessInstance processInstance = runtimeService
                        .createProcessInstanceQuery()
                        .processInstanceId(task.getProcessInstanceId())
                        .singleResult();
                UserInfoDTO user = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));

                BpmMessageSendWhenTaskCreatedReqDTO dto = BpmMessageSendWhenTaskCreatedReqDTO.builder()
                        .processInstanceId(processInstance.getProcessInstanceId())
                        .processInstanceName(processInstance.getName())
                        .startUserId(user.getId())
                        .startUserNickname(user.getNickName())
                        .taskId(task.getId())
                        .taskName(task.getName())
                        .assigneeUserId(NumberUtils.parseUpperCaseLong(task.getAssignee()))
                        .build();
                messageService.sendMessageWhenTaskAssigned(dto);
            }
        });
    }

    @Override
    protected void activityCancelled(FlowableActivityCancelledEvent event) {
        HistoricActivityInstance activity = historyService
                .createHistoricActivityInstanceQuery()
                .executionId(event.getExecutionId())
                .list().get(0);

        if (activity == null) {
            log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId());
            return;
        }
        if (StrUtil.isEmpty(activity.getTaskId())) {
            return;
        }
        // 需要在事务提交后，才进行查询。不然查询不到历史的原因
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                // 可能只是活动，不是任务，所以查询不到
                HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery()
                        .taskId(activity.getTaskId()).singleResult();
                if (task == null) {
                    return;
                }

                // 如果任务拓展表已经是完成的状态，则跳过
                BpmTaskExtDO taskExt = taskExtMapper.selectOne(new LambdaQueryWrapperBase<BpmTaskExtDO>()
                        .eq(BpmTaskExtDO::getTaskId, activity.getTaskId()));
                if (taskExt == null) {
                    log.error("[updateTaskExtCancel][taskId({}) 查找不到对应的记录，可能存在问题]", activity.getTaskId());
                    return;
                }
                // 如果已经是最终的结果，则跳过
                if (BpmProcessInstanceResultEnum.isEndResult(taskExt.getResult())) {
                    log.error("[updateTaskExtCancel][taskId({}) 处于结果({})，无需进行更新]", activity.getTaskId(), taskExt.getResult());
                    return;
                }
                BpmTaskExtDO ext = new BpmTaskExtDO();
                ext.setId(taskExt.getId());
                ext.setResult(BpmProcessInstanceResultEnum.CANCEL.getResult());
                ext.setEndTime(LocalDateTime.now());
                ext.setReason(BpmProcessInstanceDeleteReasonEnum.translateReason(task.getDeleteReason()));
                // 更新任务
                taskExtMapper.updateById(ext);
            }
        });


    }

    /**
     * Task转DO
     *
     * @param task 任务
     * @return extDO
     */
    private BpmTaskExtDO taskToTaskExt(Task task) {
        BpmTaskExtDO ext = new BpmTaskExtDO();
        ext.setTaskId(task.getId());
        ext.setAssigneeUserId(NumberUtils.parseLong(task.getAssignee()));
        ext.setName(task.getName());
        ext.setProcessDefinitionId(task.getProcessDefinitionId());
        ext.setProcessInstanceId(task.getProcessInstanceId());
        return ext;
    }

}
